#ifndef PLC2LLVM_SCOPE_H
#define PLC2LLVM_SCOPE_H

#include "plc2llvm/ScopeSystem/Table.h"
#include <plc2llvm/ObjectSystem/Object.h>
#include <plc2llvm/TypeSystem/Type.h>
#include <plc2llvm/utils/Log.h>
#include "plc2llvm/Semantic/SemanticError.h"


namespace plcst
{

    /*
     * 检查在这一层做好
     * 相关检查为：是否已经存在，是否有键冲突等等。
     */
    class Scope
    {
    public:
        using CacheObjTable = Table<Object>;
        using CacheTypeTable = Table<Type>;

        explicit Scope(Scope *p, std::shared_ptr<Object> src = std::shared_ptr<Object>());
        ~Scope() = default;

        template <typename T>
        void insert(std::shared_ptr<T> item);

        template <typename T>
        void insertCache(std::shared_ptr<T> item);

        template <typename T>
        bool is_name_conflict(std::string_view name);

        // find 
        template <typename T>
        std::shared_ptr<T> find(std::string_view name);

        template <typename T>
        std::shared_ptr<T> findCache(std::string_view name);

        Scope *getParent();

        std::shared_ptr<Object> getSrcObject();

    private:
        std::shared_ptr<Object> srcObj;
        Scope *parent;
        std::unique_ptr<Table<Object>> objTable;
        std::unique_ptr<Table<Type>> typeTable;

        std::unique_ptr<CacheObjTable> objCacheTable;
        std::unique_ptr<CacheTypeTable> typeCacheTable;
    };

    template <typename T>
    void plcst::Scope::insert(std::shared_ptr<T> item)
    {
        if constexpr (std::is_same_v<T, Object>)
        {
            if (is_name_conflict<Object>(item->getObjName()))
                // TODO: 在这里报错，因为对象名称冲突了。
                throw SemanticError("Object has duplicate name");
            else
                objTable->add(item->getObjName(), item);
        }
        else
        {
            if (is_name_conflict<Type>(item->getTypeName()))
                // TODO: 在这里报错，因为类型名称冲突了。
                throw SemanticError("Type has duplicate name");
            else
                typeTable->add(item->getTypeName(), item);
        }
    }

    template <typename T>
    void plcst::Scope::insertCache(std::shared_ptr<T> item)
    {
        if constexpr (std::is_same_v<T, Object>)
        {
            if (is_name_conflict<Object>(item->getObjName()))
                // TODO: 在这里报错，因为对象名称冲突了。
                Error << "Object name is conflicted in local scope!";
            else
                objCacheTable->add(item->getObjName(), item);
        }
        else
        {
            if (is_name_conflict<Type>(item->getTypeName()))
                // TODO: 在这里报错，因为类型名称冲突了。
                Error << "Type name is conflicted in local scope!";
            else
                typeCacheTable->add(item->getTypeName(), item);
        }
    }

    template <typename T>
    bool plcst::Scope::is_name_conflict(std::string_view name)
    {
        if constexpr (std::is_same_v<T, Object>)
            return objTable->is_exist(name);
        else
            return typeTable->is_exist(name);
    }

    template <typename T>
    std::shared_ptr<T> plcst::Scope::find(std::string_view name)
    {
        if constexpr (std::is_same_v<T, Object>)
            return objTable->find(name);
        else
            return typeTable->find(name);
    }

    template <typename T>
    std::shared_ptr<T> plcst::Scope::findCache(std::string_view name)
    {
        if constexpr (std::is_same_v<T, Object>)
            return objCacheTable->find(name);
        else
            return typeCacheTable->find(name);
    }

}

#endif // PLC2LLVM_SCOPE_H
